package com.maventools.utils;

import com.maventools.domain.Configuration;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.io.File;
import java.io.FileOutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * 实现XML和Configuration间的相互转化
 */
public class XmlUtils {

    /**
     * 通过SAX解析XML文件，返回Configuration对象
     */
    public static Configuration parseXML(File xmlFile) throws Exception {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        SettingsHandler handler = new SettingsHandler();
        parser.parse(xmlFile, handler);
        return handler.getConfiguration();
    }

    /**
     * 将Configuration对象生成对应的XML文件
     */
    public static void generateXML(Configuration config, File outputFile) throws Exception {
        // 使用W3C DOM创建XML
        Document doc = javax.xml.parsers.DocumentBuilderFactory.newInstance()
                .newDocumentBuilder().newDocument();
        Element settings = doc.createElement("settings");
        doc.appendChild(settings);

        // localRepository
        Element localRepoElem = doc.createElement("localRepository");
        localRepoElem.setTextContent(config.getLocalRepository());
        settings.appendChild(localRepoElem);

        // pluginGroups
        for (String group : config.getPluginGroups()) {
            Element pg = doc.createElement("pluginGroup");
            pg.setTextContent(group);
            settings.appendChild(pg);
        }

        // servers
        for (Configuration.Server server : config.getServers()) {
            Element serverElem = doc.createElement("server");
            appendChildWithText(doc, serverElem, "id", server.getId());
            appendChildWithText(doc, serverElem, "username", server.getUserName());
            appendChildWithText(doc, serverElem, "password", server.getPassWord());
            settings.appendChild(serverElem);
        }

        // proxies
        for (Configuration.Proxy proxy : config.getProxies()) {
            Element proxyElem = doc.createElement("proxy");
            appendChildWithText(doc, proxyElem, "id", proxy.getId());
            appendChildWithText(doc, proxyElem, "active", String.valueOf(proxy.isActive()));
            appendChildWithText(doc, proxyElem, "protocol", proxy.getProcotol());
            appendChildWithText(doc, proxyElem, "username", proxy.getUserName());
            appendChildWithText(doc, proxyElem, "password", proxy.getPassWord());
            appendChildWithText(doc, proxyElem, "host", proxy.getHost());
            appendChildWithText(doc, proxyElem, "port", proxy.getPort());
            settings.appendChild(proxyElem);
        }

        // mirrors
        for (Configuration.Mirror mirror : config.getMirrors()) {
            Element mirrorElem = doc.createElement("mirror");
            appendChildWithText(doc, mirrorElem, "id", mirror.getId());
            appendChildWithText(doc, mirrorElem, "mirrorOf", mirror.getMirrorOf());
            appendChildWithText(doc, mirrorElem, "url", mirror.getUrl().toString());
            settings.appendChild(mirrorElem);
        }

        // 使用Transformer保存为文件
        Transformer tf = TransformerFactory.newInstance().newTransformer();
        tf.setOutputProperty(OutputKeys.INDENT, "yes");
        tf.transform(new DOMSource(doc), new StreamResult(new FileOutputStream(outputFile)));
    }

    private static void appendChildWithText(Document doc, Element parent, String tagName, String text) {
        Element elem = doc.createElement(tagName);
        elem.setTextContent(text);
        parent.appendChild(elem);
    }

    /**
     * 内部类： SAX 处理器实现
     */
    static class SettingsHandler extends DefaultHandler {
        private Configuration config = new Configuration();

        private String currentElement = null;
        private StringBuilder charactersBuffer = new StringBuilder();
        // 临时对象
        private Configuration.Server serverTemp;
        private Configuration.Proxy proxyTemp;
        private Configuration.Mirror mirrorTemp;

        public Configuration getConfiguration() {
            return config;
        }

        @Override
        public void startDocument() throws SAXException {
            // 初始化
            config = new Configuration();
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            currentElement = qName;
            charactersBuffer.setLength(0);
            switch (qName) {
                case "server":
                    serverTemp = new Configuration.Server();
                    break;
                case "proxy":
                    proxyTemp = new Configuration.Proxy();
                    break;
                case "mirror":
                    mirrorTemp = new Configuration.Mirror();
                    break;
            }
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            charactersBuffer.append(ch, start, length);
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            String value = charactersBuffer.toString().trim();

            switch (qName) {
                case "localRepository":
                    config.setLocalRepository(value);
                    break;
                case "pluginGroup":
                    config.getPluginGroups().add(value);
                    break;
                case "server":
                    if (serverTemp != null) {
                        config.getServers().add(serverTemp);
                        serverTemp = null;
                    }
                    break;
                case "id":
                    if (serverTemp != null) {
                        serverTemp.setId(value);
                    } else if (mirrorTemp != null) {
                        mirrorTemp.setId(value);
                    } else if (proxyTemp != null) {
                        proxyTemp.setId(value);
                    }
                    break;
                case "username":
                    if (serverTemp != null) {
                        serverTemp.setUserName(value);
                    } else if (proxyTemp != null) {
                        proxyTemp.setUserName(value);
                    }
                    break;
                case "password":
                    if (serverTemp != null) {
                        serverTemp.setPassWord(value);
                    } else if (proxyTemp != null) {
                        proxyTemp.setPassWord(value);
                    }
                    break;
                case "host":
                    if (proxyTemp != null) {
                        proxyTemp.setHost(value);
                    }
                    break;
                case "port":
                    if (proxyTemp != null) {
                        proxyTemp.setPort(value);
                    }
                    break;
                case "active":
                    if (proxyTemp != null) {
                        proxyTemp.setActive(Boolean.parseBoolean(value));
                    }
                    break;
                case "protocol":
                    if (proxyTemp != null) {
                        proxyTemp.setProcotol(value);
                    }
                    break;
                case "mirror":
                    if (mirrorTemp != null) {
                        config.getMirrors().add(mirrorTemp);
                        mirrorTemp = null;
                    }
                    break;
                case "mirrorOf":
                    if (mirrorTemp != null) {
                        mirrorTemp.setMirrorOf(value);
                    }
                    break;
                case "url":
                    if (mirrorTemp != null) {
                        try {
                            mirrorTemp.setUrl(new URL(value));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    break;
            }
        }
    }
}
